﻿using DotnetFramework.CAP.IocRefction;
using DotNetFramework.CAP.Core.Ioc;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace DotnetFramework.CAP.Ioc
{
    public class ServiceCollection : IServiceCollection
    {
        private List<ServiceDescriptor> ListServiceDescriptor;

        IocRefction.ContainerBuilder _builder;
        public IContainer Container { get; set; }
        public DotNetFramework.CAP.Core.Ioc.IServiceProvider ServiceProvider { get; set; }

        public ServiceCollection()
        {
            _builder = new ContainerBuilder();
            ListServiceDescriptor = new List<ServiceDescriptor>();
        }

        public void BeginRegister()
        {
            Container = _builder.Build();
            var provider = new ServiceProvider(Container);
            ServiceProvider = provider;
            this.AddServiceInstance(provider);
            ServiceProvider.ServiceCollection = this;
        }

        public IServiceCollection AddServiceInstance(ServiceProvider instance)
        {
            ListServiceDescriptor.Add(new ServiceDescriptor(instance.GetType(), typeof(DotNetFramework.CAP.Core.Ioc.IServiceProvider)));

            _builder.RegisterInstance<ServiceProvider, DotNetFramework.CAP.Core.Ioc.IServiceProvider>(instance);
            return this;
        }
        public IServiceCollection AddInstance<TService>(TService instance) where TService : class
        {
            ListServiceDescriptor.Add(new ServiceDescriptor(instance.GetType(), typeof(TService)));
            _builder.RegisterInstance<TService, TService>(instance);
            return this;
        }

        public IServiceCollection AddScoped(Type serviceType, Type implementationType)
        {
            ListServiceDescriptor.Add(new ServiceDescriptor(implementationType, serviceType));
            _builder.RegisterGeneric(implementationType, serviceType);
            return this;
        }

        public IServiceCollection AddScoped<TService, TImplementation>()
        {
            ListServiceDescriptor.Add(new ServiceDescriptor(typeof(TImplementation), typeof(TService)));
            _builder.RegisterType<TImplementation, TService>();
            return this;
        }

        public IServiceCollection AddSingleton<TService>(TService implementationInstance) where TService : class
        {
            ListServiceDescriptor.Add(new ServiceDescriptor(implementationInstance.GetType(), typeof(TService)));
            _builder.RegisterInstance<TService, TService>(implementationInstance);
            //_builder.RegisterInstance(implementationInstance).As<TService>().SingleInstance();
            return this;
        }

        public IServiceCollection AddSingleton(Type serviceType, Type implementationType)
        {
            ListServiceDescriptor.Add(new ServiceDescriptor(implementationType, serviceType));
            _builder.RegisterGeneric(implementationType, serviceType, true);
            //_builder.RegisterGeneric(implementationType).As(serviceType).SingleInstance();
            return this;
        }

        public IServiceCollection AddSingleton<TService>() where TService : class
        {
            ListServiceDescriptor.Add(new ServiceDescriptor(typeof(TService), typeof(TService)));
            _builder.RegisterType<TService>(true);
            //_builder.RegisterType<TService>().SingleInstance();
            return this;
        }

        public IServiceCollection AddTransient<TService, TImplementation>()
        {
            ListServiceDescriptor.Add(new ServiceDescriptor(typeof(TImplementation), typeof(TService)));
            _builder.RegisterType<TImplementation, TService>();
            //_builder.RegisterType<TImplementation>().As<TService>();
            return this;
        }

        public void TryAddSingleton<TService>() where TService : class
        {
            ListServiceDescriptor.Add(new ServiceDescriptor(typeof(TService), typeof(TService)));
            _builder.RegisterType<TService>(true);
            //_builder.RegisterType<TService>().SingleInstance();
        }

        void IServiceCollection.TryAddSingleton<TService, TImplementation>()
        {

            ListServiceDescriptor.Add(new ServiceDescriptor(typeof(TImplementation), typeof(TService)));
            _builder.RegisterType<TImplementation, TService>(true);
            //_builder.RegisterType<TImplementation>().As<TService>().SingleInstance();
        }

        IServiceCollection IServiceCollection.AddSingleton<TService, TImplementation>()
        {
            ListServiceDescriptor.Add(new ServiceDescriptor(typeof(TImplementation), typeof(TService)));
            _builder.RegisterType<TImplementation, TService>(true);
            //_builder.RegisterType<TImplementation>().As<TService>().SingleInstance();
            return this;
        }

        public IServiceCollection AddScopedMuti<TService, TImplementation>()
        {
            ListServiceDescriptor.Add(new ServiceDescriptor(typeof(TImplementation), typeof(TService)));
            _builder.RegisterTypeNamed<TImplementation, TService>(typeof(TImplementation).FullName);
            //_builder.RegisterType<TImplementation>().Named<TService>(typeof(TImplementation).Name);
            return this;
        }
        public IServiceCollection AddScopedMuti(Type TService, Type TImplementation)
        {
            ListServiceDescriptor.Add(new ServiceDescriptor(TImplementation, TService));
            _builder.RegisterGenericNamed(TImplementation, TService, TImplementation.FullName, false);
            return this;
        }


        public IServiceCollection AddSingleton<TService>(Func<DotNetFramework.CAP.Core.Ioc.IServiceProvider, TService> implementationFactory) where TService : class
        {
            implementationFactory?.Invoke(ServiceProvider);
            return this;
        }

        public IEnumerator<ServiceDescriptor> GetEnumerator()
        {
            foreach (var item in ListServiceDescriptor)
            {
                yield return item;
            }
        }

        public void TryAddEnumerable(ServiceDescriptor descriptor)
        {
            _builder.TryAddEnumerable(descriptor.ImplementationType, descriptor.ServiceType);
            //_builder.RegisterType(descriptor.ImplementationType).As(descriptor.ServiceType);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            foreach (var item in ListServiceDescriptor)
            {
                yield return item;
            }
        }
    }
}
